#pragma once

#include <stdint.h>

#ifdef __cplusplus
extern "C"
{
#endif

  typedef int ca_iter;
  typedef uint32_t usize_t;
  typedef void* CADATAPTR;

  typedef struct CircleArray
  {
    ca_iter s;
    ca_iter e;
    ca_iter c;
    usize_t siz;
    usize_t item_size;
    usize_t data_size;
    CADATAPTR data_ptr;
  } CircleArray;

#ifndef CIRCLE_ARRAY_END
#define CIRCLE_ARRAY_END
#define ca_iter_end -1
#endif // CIRCLE_ARRAY_END

  void init_circle_array(CircleArray* ca,
                         CADATAPTR data,
                         usize_t item_size,
                         usize_t array_size);

  CircleArray* create_circle_array(const usize_t item_size,
                                   const usize_t array_size);
  void release_circle_array(CircleArray** ca);
  CADATAPTR ca_get_data_ptr(const CircleArray* ca);
  CADATAPTR ca_get_data(const CircleArray* ca, const ca_iter it);
  ca_iter ca_front(const CircleArray* ca);
  ca_iter ca_back(const CircleArray* ca);
  ca_iter ca_current(const CircleArray* ca);
  ca_iter ca_next(const CircleArray* ca, const ca_iter it);
  ca_iter ca_prev(const CircleArray* ca, const ca_iter it);
  ca_iter ca_new_data(CircleArray* ca, const CADATAPTR data);

  ca_iter ca_pop_back(CircleArray* ca);
  ca_iter ca_pop_front(CircleArray* ca);
  ca_iter ca_delete(CircleArray* ca, const ca_iter it);

  CADATAPTR ca_data(const CircleArray* ca, const int index);
  int ca_remove(CircleArray* ca, const int index);

  void ca_clear(CircleArray* ca);

#define CA_GET_DATA(Type, ca, it) ((Type*)(ca_get_data(ca, it)))

#ifdef __cplusplus
}
#endif

#ifdef __cplusplus

template<typename T>
struct CircleVector
{
  CircleVector() noexcept
  {
    ca = (CircleArray*)(create_circle_array(sizeof(T), deep_));
  }

  CircleVector(const usize_t array_size) noexcept
  {
    ca = (CircleArray*)(create_circle_array(sizeof(T), array_size));
  }

  ~CircleVector() noexcept { release_circle_array(&ca); }

  bool init_data(const usize_t array_size) noexcept
  {
    ca = (CircleArray*)(create_circle_array(sizeof(T), array_size));
    return (ca != nullptr);
  }

  T* get_data_ptr() const noexcept { return ca->data_ptr; }

  T* get_data(const ca_iter it) { return (T*)(ca_get_data(ca, it)); }
  T& get(const ca_iter it) { return (*(T*)(ca_get_data(ca, it))); }

  ca_iter it_front() const noexcept { return ca_front(ca); }
  ca_iter it_back() const noexcept { return ca_back(ca); }
  ca_iter it_current() const noexcept { return ca_current(ca); }
  ca_iter it_next(const ca_iter it) const noexcept { return ca_next(ca, it); }
  ca_iter it_prev(const ca_iter it) const noexcept { return ca_prev(ca, it); }

  T* front() const noexcept
  {
    auto it = ca_front(ca);
    return (it == ca_iter_end) ? nullptr : ((T*)(ca_get_data(ca, it)));
  }

  T* back() const noexcept
  {
    auto it = ca_back(ca);
    return (it == ca_iter_end) ? nullptr : ((T*)(ca_get_data(ca, it)));
  }

  T* current() const noexcept
  {
    auto it = ca_current(ca);
    return (it == ca_iter_end) ? nullptr : ((T*)(ca_get_data(ca, it)));
  }

  ca_iter new_data(const T* new_data) noexcept
  {
    return ca_new_data(ca, (const CADATAPTR)(new_data));
  }

  ca_iter new_data(const T& new_data) noexcept
  {
    return ca_new_data(ca, (const CADATAPTR)(&new_data));
  }

  void clear() noexcept { return ca_clear(ca); }

  ca_iter pop_back() noexcept { return ca_pop_back(ca); }

  ca_iter pop_front() noexcept { return ca_pop_front(ca); }

  ca_iter del(const ca_iter it) noexcept { return ca_delete(ca, it); }

  T* data(const int index) noexcept { return ca_data(ca, index); }
  T& the_data(const int index) noexcept { return *(T*)(ca_data(ca, index)); }

  int remove(const int index) noexcept { return ca_remove(ca, index); }

private:
  usize_t deep_ = 100;
  CircleArray* ca = nullptr;
};

#endif